#include "stdafx.h"
#include "Radiosity.h"
#include "Parameters.h"
#include "Mesh.h"
#include "Pushers.h"
#include "resource.h"
#include <Utilities\Extensions.h>
#include <Utilities\Camera.h>

using namespace std;
using namespace std::tr1;

const array<GLshort[2], 4> Radiosity::_normalizedCoords = {0, 0, 1, 0, 1, 1, 0, 1};

bool Radiosity::_frameDrawFlag = false;
bool Radiosity::_fovChangedFlag = false;
float Radiosity::_fovDecayAngle = 0;

Radiosity::Radiosity(const Parameters* const parameters)
:_parameters(parameters), _camera(NULL)
{
	Create();
}

Radiosity::Radiosity(const Parameters* const parameters, const Camera* const camera)
:_parameters(parameters), _camera(camera)
{
	Create();
}

void Radiosity::Create()
{
	_shootIndex = Vec2i(0, 0);
	_shootSize = Vec2i(0, 0);
	_shooter.handle() = RadiosityMesh::InvalidFaceHandle;

	_frameDrawFlag = false;
	_importanceShootFlag = true;
	_shootingUnshotRadiosity = false;
	_fovChangedFlag = false;
	_shootMode = ProgressiveRefinement;
	_fovDecayAngle = 0;

	//Set class member parameters to point to globals, which are allocated from the main application via the registry
	_mesh.Create(_parameters);

	_FBO.Bind();

	InitializeOpenGL();
	InitializeShaders();
	InitializeMatrixes();
	//Sets up the FBO
	InitializeBuffers();
	InitializeTextures();
	//Initializes emissivity, importance (if needed), and shoot parameters
	InitializeFaces();

	//If we're using ambient, compute it before the first display
	if(_parameters->GetDisplayAmbient())
	{
		//Initializes parameters needed for an ambient term display
		InitializeAmbientParameters();
		DrawAmbientValue();
	}
	//Disable FBO until iterate is ready to draw into it
	FramebufferObject::Disable();
}

void Radiosity::Display(void)
{
	PushAttribute textureBit(GL_TEXTURE_BIT);
	PushAttribute enableBit(GL_ENABLE_BIT);
	PushShader displayShader(_displayShader);

	//Disable all FBO operations
	FramebufferObject::Disable();
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glEnable(GL_TEXTURE_2D);
	_ambient.BindTexture(GL_TEXTURE1);
	for(RadiosityMesh::FaceIter faceIter = _mesh.faces_begin(); faceIter != _mesh.faces_end(); ++faceIter)
	{
		//Bind the radiosity texture
		bool bindImportanceTexture = (_parameters->GetRadiosityAlgorithm() == ImportanceDriven) && (_parameters->GetDisplayVisualization());
		faceIter->GetRadiosityTextureVariable(bindImportanceTexture ? Ij : Bj).BindTexture(GL_TEXTURE0);
#ifndef _DEBUG
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
#endif
		if(_parameters->GetDisplayVisualization())
		{
			_displayShader.SetUniform1i("useDebugColor", false);
			if((_shooter.handle() != RadiosityMesh::InvalidFaceHandle) && (_parameters->GetRadiosityAlgorithm() == ProgressiveRefinement))
			{
				_displayShader.SetUniform1i("useDebugColor", faceIter->GetIsVisibleHemisphere(_mesh.GetItemBufferMesh()) || faceIter.handle() == _shooter.handle());
				_displayShader.SetUniform3fv("debugColor", (faceIter.handle() == _shooter.handle()) ? Vec3f(1, 0, 0.5).data() : Vec3f(1, 1, 0).data());
			}
			else if((_shooter.handle() != RadiosityMesh::InvalidFaceHandle) && (_parameters->GetRadiosityAlgorithm() == FovDriven))
			{
				_displayShader.SetUniform1i("useDebugColor", _mesh.GetIsVisibleFov(faceIter) || faceIter.handle() == _shooter.handle());
				_displayShader.SetUniform3fv("debugColor", (faceIter.handle() == _shooter.handle()) ? Vec3f(1, 0, 0.5).data() : Vec3f(1, 1, 0).data());
			}
		}

		PushDrawCall quads(GL_QUADS);
		array<GLshort[2], 4>::const_iterator texCoordsIter;
		RadiosityMesh::FaceVertexIter faceVertexIter;
		glColor3fv(faceIter->GetFaceReflectivity().data());
		for(faceVertexIter = _mesh.fv_iter(faceIter.handle()), texCoordsIter = _normalizedCoords.begin(); faceVertexIter; ++faceVertexIter, texCoordsIter++)
		{
			//Map the textures to the faces 1-to-1
			glTexCoord2sv(*texCoordsIter);
			glVertex3fv(_mesh.point(faceVertexIter).data());
		}
	}
}

bool Radiosity::Iterate(void)
{
	_FBO.Bind();

	do
	{
		if(_parameters->GetRadiosityAlgorithm() != ProgressiveRefinement  && !_shootingUnshotRadiosity)
		{
			PrepareForUnshotShooting();
		}

		if(_shootingUnshotRadiosity)
		{
			ShootUnshotVectors();
		}

		if(!_shootingUnshotRadiosity)
		{
			if(!AdvanceIteration())
			{
				return false;
			}

			ShootFromElement();
			UpdateShootIndices();			
		}
	}
	while(!_frameDrawFlag);

	_frameDrawFlag = false;
	//Disable FBO until iterate is ready to draw into it again
	FramebufferObject::Disable();
	//Not done yet
	return true;
}

void Radiosity::ShootFromElement(void)
{
	PushAttribute transformBit(GL_TRANSFORM_BIT);
	PushMatrix modelview(GL_MODELVIEW);

	Vec2f shootTexCoords;
	Vec3f shootPosition;
	//Get the eye position from the texture values
	GetShootParameters(_shootIndex, shootPosition, shootTexCoords);

	//Draws the stereographic item buffer from the shooter's POV
	DrawHemisphereItemBuffer(_shooter, shootPosition);

	//If doing FovDriven, updates the unshot vectors using the new shooter's parameters
	if(_parameters->GetRadiosityAlgorithm() == FovDriven)
	{
		GatherUnshotVectors(shootTexCoords);
	}

	//Shades all the faces that can be seen from the shooter's POV, using the item buffer
	HemisphereShadeFromTexture(shootPosition, shootTexCoords);
}

void Radiosity::PrepareForUnshotShooting(void)
{
	//This step happens initially, or whenever the viewing parameters change and the importance shoot flag is
	//set.  It is okay to shoot new importance while in the middle of shooting a face's radiosity, but not while
	//shooting the importance, so we can't force a redraw during that.  This is also in keeping with the importance algorithm.
	ShootDirectlyReceivedImportance();

	if(_parameters->GetRadiosityAlgorithm() == FovDriven && _fovChangedFlag)
	{
		FramebufferObject::Disable();
		DrawFovItemBuffer();
		StreamVisibilityVector();
		_FBO.Bind();

		RetrieveUnshotVectors();
		ReadBecomingVisibleVectors();

		_currentUnshotReceiver = _mesh.faces_begin();
		_shootingUnshotRadiosity = true;
		_fovChangedFlag = false;
	}
}

bool Radiosity::AdvanceIteration(void)
{
	if(_shootIndex == _shootSize)
	{
		_shootIndex = Vec2i(0, 0);

		//If this is progressive refinement, the shooting mode never changes, but importance driven switches back and forth between the two.
		SelectShootingMode();

		//Select the next shooting face by using a z-buffer algorithm
		_shooter = GetNextShooter();
		//Check to see if we're done
		if(_shooter.handle() == RadiosityMesh::InvalidFaceHandle)
		{
			return false;
		}

		//Load up the new face's positions and texture coordinates
		LoadShootParameters();
	}
	return true;
}


void Radiosity::GetShootParameters(const Vec2i& shootMipIndex, Vec3f& shootPosition, Vec2f& shootTexCoord)
{
	for(int k = 0; k < 3; k++)
	{
		shootPosition[k] = _shootPositions[(shootMipIndex[Width] * _shootSize[Height] * 3) + (shootMipIndex[Height] * 3) + k];
	}

	for(int k = 0; k < 2; k++)
	{
		shootTexCoord[k] = _shootTexCoords[(shootMipIndex[Width] * _shootSize[Height] * 3) + (shootMipIndex[Height] * 3) + k];
	}
}

void Radiosity::ShootDirectlyReceivedImportance(void)
{
	if(_importanceShootFlag)
	{
		PushAttribute transformBit(GL_TRANSFORM_BIT);
		PushMatrix modelview(GL_MODELVIEW);

		DrawUserViewItemBuffer();
		UserViewShade();

		_importanceShootFlag = false;
	}
}

void Radiosity::ShootUnshotVectors(void)
{
	vector<Vec4f>& unshotShootPositions = _currentUnshotReceiver->GetUnshotShootPositions();
	for(vector<Vec4f>::iterator iter = unshotShootPositions.begin(); iter != unshotShootPositions.end(); iter++)
	{
		ShootUnshotRadiosityFromPoint(_currentUnshotReceiver, *iter);
	}
	unshotShootPositions.clear();

	if(++_currentUnshotReceiver == _mesh.faces_end())
	{
		_shootingUnshotRadiosity = false;
	}
}

void Radiosity::ShootUnshotRadiosityFromPoint(const RadiosityMesh::FaceIter faceIter, const Vec4f unshotShootPosition)
{
	PushAttribute transformBit(GL_TRANSFORM_BIT);
	PushMatrix modelview(GL_MODELVIEW);

	Vec3f shootPosition(unshotShootPosition[0], unshotShootPosition[1], unshotShootPosition[2]);
	float shooterOffset = unshotShootPosition[3];
	DrawHemisphereItemBuffer(faceIter, shootPosition);
	HemisphereShadeFromPoint(faceIter, shootPosition, shooterOffset);
}

void Radiosity::SelectShootingMode(void)
{
	if(_parameters->GetRadiosityAlgorithm() != ProgressiveRefinement)
	{
		_shootMode = (_shootMode == ProgressiveRefinement) ? ImportanceDriven : ProgressiveRefinement;
	}
}

void Radiosity::DrawHemisphereItemBuffer(const RadiosityMesh::FaceIter& shooter, const Vec3f& shootPosition)
{
	PushAttribute colorBufferBit(GL_COLOR_BUFFER_BIT);
	PushAttribute pixelModeBit(GL_PIXEL_MODE_BIT);
	PushAttribute viewportBit(GL_VIEWPORT_BIT);
	PushShader stereographic(_stereographicShader);
	PushFBOAttachment hemisphereItemBuffer(_FBO, GL_COLOR_ATTACHMENT0_EXT, _hemisphereItemBuffer);
	PushFBOAttachment hemisphereItemBufferRBO(_FBO, GL_DEPTH_ATTACHMENT_EXT, _hemisphereItemBufferRBO);

	glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
	glViewport(0, 0, _parameters->GetItemBufferResolution(), _parameters->GetItemBufferResolution());

	//Compute the center of the view as the eye point plus the face's normal
	Vec3f shootCenter = shootPosition + _mesh.normal(shooter);
	//Compute the VUP vector from the first vertex of the face to the eye point
	Vec3f shootVUP = (_mesh.point(_mesh.fv_iter(shooter.handle())) - shootPosition).normalize();
	//This transformation goes to the modelview matrix.
	gluLookAt(shootPosition[0], shootPosition[1], shootPosition[2], shootCenter[0], shootCenter[1], shootCenter[2], shootVUP[0], shootVUP[1], shootVUP[2]);

	glClearColorIiEXT(-1, -1, -1, 1);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	for(RadiosityMesh::FaceIter faceIter = _mesh.faces_begin(); faceIter != _mesh.faces_end(); ++faceIter)
	{
		if(faceIter.handle().idx() == shooter.handle().idx())
		{
			continue;
		}

		_stereographicShader.SetUniform1i("faceId", faceIter.handle().idx());
		for (vector<ItemBufferMesh::FaceHandle>::const_iterator iter = faceIter->GetChildFaceHandles().begin(); iter != faceIter->GetChildFaceHandles().end(); iter++)
		{
			ItemBufferMesh::ConstFaceIter childFaceIter(_mesh.GetItemBufferMesh(), *iter);
			PushQuery samplesPassed(childFaceIter->GetItemBufferOcclusionQuery());
			PushDrawCall quads(GL_QUADS);
			for(ItemBufferMesh::ConstFaceVertexIter item_fv_it = _mesh.GetItemBufferMesh().cfv_iter(*iter); item_fv_it; ++item_fv_it)
			{
				glVertex3fv(_mesh.GetItemBufferMesh().point(item_fv_it).data());
			}
		}
	}
}

void Radiosity::DrawFovItemBuffer(void)
{
	PushAttribute transformBit(GL_TRANSFORM_BIT);
	PushMatrix modelview(GL_MODELVIEW);
	PushAttribute colorBufferBit(GL_COLOR_BUFFER_BIT);
	PushAttribute pixelModeBit(GL_PIXEL_MODE_BIT);
	PushAttribute viewportBit(GL_VIEWPORT_BIT);

	glViewport(0, 0, _parameters->GetWindowWidth(), _parameters->GetWindowHeight());
	glDrawBuffer(GL_AUX0);
	_camera->PerspectiveDisplay(_parameters->GetWindowWidth(), _parameters->GetWindowHeight(), GetCurrentFov());

	glClear(GL_DEPTH_BUFFER_BIT);

	for(RadiosityMesh::FaceIter faceIter = _mesh.faces_begin(); faceIter != _mesh.faces_end(); ++faceIter)
	{
		PushQuery query(_mesh.GetFovOcclusionQuery(faceIter));
		PushDrawCall quads(GL_QUADS);
		for(RadiosityMesh::FaceVertexIter faceVertexIter = _mesh.fv_iter(faceIter.handle()); faceVertexIter; ++faceVertexIter)
		{
			glVertex3fv(_mesh.point(faceVertexIter).data());
		}
	}
}

void Radiosity::StreamVisibilityVector(void)
{
	_fovVisibleVector.BindTexture();
	_fovVisibleVectorPBO.Bind();

	_mesh.GetFovOcclusionQueryResults((GLubyte*)_fovVisibleVectorPBO.MapResetBuffer());
	PixelbufferObject::UnmapBuffer();

	glTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, _mesh.n_faces(), 1, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0);
	PixelbufferObject::Disable();
}

void Radiosity::GatherUnshotVectors(const Vec2f& shootTexCoords)
{
	PushAttribute viewportBit(GL_VIEWPORT_BIT);
	PushAttribute colorBufferBit(GL_COLOR_BUFFER_BIT);
	PushAttribute transformBit(GL_TRANSFORM_BIT);
	PushAttribute textureBit(GL_TEXTURE_BIT);
	PushShader unshotVectorsGather(_unshotVectorsGatherShader);
	PushFBOAttachmentRectRW unshotVectors(_FBO, GL_COLOR_ATTACHMENT0_EXT, _unshotVectors);

	Vec2i unshotVectorsSize = _unshotVectors.GetTextureSize();
	glViewport(0, 0, unshotVectorsSize[Width], unshotVectorsSize[Height]);
	glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);

	_unshotVectors.BindTexture(GL_TEXTURE0);
	_fovVisibleVector.BindTexture(GL_TEXTURE1);
	_mesh.GetFacePositionsTextureVariable().BindTexture(GL_TEXTURE2);
	_shooter->GetRadiosityTextureVariableDelta(_shootMode).BindTexture(GL_TEXTURE3);

	_unshotVectorsGatherShader.SetUniform1i("shooterId", _shooter.handle().idx());
	_unshotVectorsGatherShader.SetUniform2fv("shooterDeltaBjTexCoords", shootTexCoords.data());
	_unshotVectorsGatherShader.SetUniform1i("shooterLevel", min(_parameters->GetShootLevel(), _shooter->GetRadiosityTextureVariableDelta(_shootMode).GetTopLevel()));
	DrawGatherUnshotVectors();

	_unshotVectors.SwapReadWrite();
}

void Radiosity::DrawGatherUnshotVectors(void)
{
	Vec2i unshotVectorsSize = _unshotVectors.GetTextureSize();
	array<GLshort[2], 4> unshotVectorsTexCoords = { 0, 0, static_cast<GLshort>(unshotVectorsSize[Width]), 0, static_cast<GLshort>(unshotVectorsSize[Width]), static_cast<GLshort>(unshotVectorsSize[Height]), 0, static_cast<GLshort>(unshotVectorsSize[Height]) };

	PushDrawCall quads(GL_QUADS);
	array<GLshort[2], 4>::const_iterator texCoordsIter;
	array<GLshort[2], 4>::const_iterator unshotVectorsCoordsIter;
	for(texCoordsIter = _normalizedCoords.begin(), unshotVectorsCoordsIter = unshotVectorsTexCoords.begin(); texCoordsIter != _normalizedCoords.end(); texCoordsIter++, unshotVectorsCoordsIter++)
	{
		glTexCoord2sv(*unshotVectorsCoordsIter);
		glVertex2sv(*texCoordsIter); 
	}
}

void Radiosity::RetrieveUnshotVectors(void)
{
	PushAttribute viewportBit(GL_VIEWPORT_BIT);
	PushAttribute colorBufferBit(GL_COLOR_BUFFER_BIT);
	PushAttribute transformBit(GL_TRANSFORM_BIT);
	PushMatrix modelview(GL_MODELVIEW);
	PushShader unshotVectorsRetrieve(_unshotVectorsRetrieveShader);
	PushFBOAttachmentRectRW becomingVisibleVectors(_FBO, GL_COLOR_ATTACHMENT0_EXT, _becomingVisibleVectors);
	PushFBOAttachmentRectRW unshotVectors(_FBO, GL_COLOR_ATTACHMENT1_EXT, _unshotVectors);

	Vec2i unshotVectorsSize = _unshotVectors.GetTextureSize();
	array<GLenum, 2> drawBuffers = {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT};
	glViewport(0, 0, unshotVectorsSize[Width], unshotVectorsSize[Height]);
	glDrawBuffers(static_cast<GLsizei>(drawBuffers.size()), &drawBuffers[0]);
	gluOrtho2D(0, 1, 0, 1);

	DrawRetrieveUnshotVectors();

	_unshotVectors.SwapReadWrite();
	_becomingVisibleVectors.SwapReadWrite();
}

void Radiosity::DrawRetrieveUnshotVectors(void)
{
	PushAttribute textureBit(GL_TEXTURE_BIT);

	_unshotVectors.BindTexture(GL_TEXTURE0);
	_fovVisibleVector.BindTexture(GL_TEXTURE1);

	Vec2i unshotVectorsSize = _unshotVectors.GetTextureSize();
	array<GLshort[2], 4> unshotVectorsTexCoords = { 0, 0, static_cast<GLshort>(unshotVectorsSize[Width]), 0, static_cast<GLshort>(unshotVectorsSize[Width]), static_cast<GLshort>(unshotVectorsSize[Height]), 0, static_cast<GLshort>(unshotVectorsSize[Height]) };

	PushDrawCall quads(GL_QUADS);
	array<GLshort[2], 4>::const_iterator texCoordsIter;
	array<GLshort[2], 4>::const_iterator unshotVectorsCoordsIter;
	for(texCoordsIter = _normalizedCoords.begin(), unshotVectorsCoordsIter = unshotVectorsTexCoords.begin(); texCoordsIter != _normalizedCoords.end(); texCoordsIter++, unshotVectorsCoordsIter++)
	{
		glTexCoord2sv(*unshotVectorsCoordsIter);
		glVertex2sv(*texCoordsIter); 
	}
}

void Radiosity::ReadBecomingVisibleVectors(void)
{
	PushAttribute pixelModeBit(GL_PIXEL_MODE_BIT);
	PushFBOAttachment becomingVisibleVectors(_FBO, GL_COLOR_ATTACHMENT0_EXT, _becomingVisibleVectors);

	Vec2i unshotMipmapSize = _mesh.GetFacePositionsTextureVariable().GetTextureSize(_parameters->GetUnshotMipmapShootLevel());
	int unshotMipmapArea = unshotMipmapSize[Width] * unshotMipmapSize[Height];
	vector<float> shootPositionsVector(_mesh.n_faces() * unshotMipmapArea * 3);
	glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
	glReadPixels(1, 0, _mesh.n_faces() * unshotMipmapArea, 1, GL_RGB, GL_FLOAT, &shootPositionsVector[0]);

	vector<float>::const_iterator shootPositionsVectorIter = shootPositionsVector.begin();
	for(RadiosityMesh::FaceIter faceIter = _mesh.faces_begin(); faceIter != _mesh.faces_end(); ++faceIter)
	{
		vector<Vec4f>& unshotShootPositions = faceIter->GetUnshotShootPositions();
		for(int i = 0; i < unshotMipmapArea; i++, shootPositionsVectorIter += 3)
		{
			Vec4f shootPosition(*shootPositionsVectorIter, *(shootPositionsVectorIter + 1), *(shootPositionsVectorIter + 2), static_cast<float>(i));
			if(shootPosition != Vec4f(0, 0, 0, static_cast<float>(i)))
			{
				unshotShootPositions.push_back(shootPosition);
			}
		}
	}
}

void Radiosity::HemisphereShadeFromTexture(const Vec3f& shootPosition, const Vec2f& shootTexCoords)
{
	PushAttribute textureBit(GL_TEXTURE_BIT);
	PushShader hemisphereTextureShader(_hemisphereTextureShader);

	//Send face-as-texture dependent parameters to shader
	_hemisphereTextureShader.SetUniform2fv("shooterDeltaBjTexCoords", shootTexCoords.data());

	HemisphereShade(_hemisphereTextureShader, _shootMode, _shooter, shootPosition);
}

void Radiosity::HemisphereShadeFromPoint(const RadiosityMesh::FaceIter& shooter, const Vec3f& shootPosition, const float shooterOffset)
{
	PushAttribute textureBit(GL_TEXTURE_BIT);
	PushShader hemispherePointShader(_hemispherePointShader);

	_becomingVisibleVectors.BindTexture(GL_TEXTURE4);
	_mesh.GetFacePositionsTextureVariable().BindTexture(GL_TEXTURE5);

	_hemispherePointShader.SetUniform1i("shooterIndex", shooter.handle().idx());
	_hemispherePointShader.SetUniform1f("shooterOffset", shooterOffset);

	HemisphereShade(_hemispherePointShader, ProgressiveRefinement, shooter, shootPosition);
}

void Radiosity::HemisphereShade(const Shader& hemisphereShader, const RadiosityAlgorithm shootMode, const RadiosityMesh::FaceIter& shooter, const Vec3f& shootPosition)
{
	PushAttribute colorBufferBit(GL_COLOR_BUFFER_BIT);
	PushAttribute viewportBit(GL_VIEWPORT_BIT);

	//Setup multiple render targets
	array<GLenum, 2> drawBuffers = {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT};
	glDrawBuffers(static_cast<GLsizei>(drawBuffers.size()), &drawBuffers[0]);

	//Bind item buffer to first shader input (in vertex shader)
	_hemisphereItemBuffer.BindTexture(GL_TEXTURE0);
	//Bind the shooting delta texture to third shader input
	shooter->GetRadiosityTextureVariableDelta(shootMode).BindTexture(GL_TEXTURE3);


	//Compute parameters to shoot from user-selected level of mipmap
	int shootLevel = min(_parameters->GetShootLevel(), shooter->GetRadiosityTextureVariableDelta(shootMode).GetTopLevel());

	hemisphereShader.SetUniform1f("shooterFaceArea", shooter->GetFaceArea());
	hemisphereShader.SetUniform3fv("shootPosition", shootPosition.data());
	hemisphereShader.SetUniform3fv("shootDirectionVector", _mesh.normal(shooter).data());
	hemisphereShader.SetUniform1i("shooterLevel", shootLevel);

	//Go through each face in the mesh, then go through each face's tesselated quads
	for(RadiosityMesh::FaceIter faceIter = _mesh.faces_begin(); faceIter != _mesh.faces_end(); ++faceIter)
	{
		//See if we can early out from shading this face
		bool faceIsShooter = faceIter.handle().idx() == shooter.handle().idx();
		bool faceIsNotInHemisphere = !faceIter->GetIsVisibleHemisphere(_mesh.GetItemBufferMesh());
		bool faceIsNotInFov = (_parameters->GetRadiosityAlgorithm() == FovDriven) && !_mesh.GetIsVisibleFov(faceIter);
		if(faceIsShooter || faceIsNotInHemisphere || faceIsNotInFov)
		{
			continue;
		}

		//Get relevant information and objects for face about to be shaded
		int faceId = faceIter.handle().idx();
		Vec2i textureSize = faceIter->GetTextureSize(shootMode);
		Vec3f normal = _mesh.normal(faceIter);
		Vec3f reflectivity = faceIter->GetFaceReflectivity();
		Texture2DRW& accumulatedRadiosity = faceIter->GetRadiosityTextureVariableAccumulated(shootMode);
		Texture2DRW& deltaRadiosity = faceIter->GetRadiosityTextureVariableDelta(shootMode);

		//Set viewport for 1-to-1 texel shading
		glViewport(0, 0, textureSize[Width], textureSize[Height]);

		//Attach output textures to FBO
		PushFBOAttachment2DRW accumulated(_FBO, GL_COLOR_ATTACHMENT0_EXT, accumulatedRadiosity);
		PushFBOAttachment2DRW delta(_FBO, GL_COLOR_ATTACHMENT1_EXT, deltaRadiosity);
		accumulatedRadiosity.BindTexture(GL_TEXTURE1);
		deltaRadiosity.BindTexture(GL_TEXTURE2);

		hemisphereShader.SetUniform3fv("receiverNormal", normal.data());
		hemisphereShader.SetUniform1i("faceId", faceId);
		hemisphereShader.SetUniform3fv("ro", (shootMode == ProgressiveRefinement) ? reflectivity.data() : Vec3f(1.0f, 1.0f, 1.0f).data());
		if(_hemispherePointShader.IsEnabled())
		{
			_hemispherePointShader.SetUniform1i("receiverIndex", faceId);
		}

		//Draw tesselated faces, which get backprojected into stereographic item buffer
		DrawTesselatedFace(faceIter);

		accumulatedRadiosity.SwapReadWrite();
		deltaRadiosity.SwapReadWrite();

		faceIter->SetHasUnshotRadiosity(true, shootMode);
	}
}

void Radiosity::DrawTesselatedFace(const RadiosityMesh::FaceIter& faceIter)
{
	//Go through each quad in the tesselated face mesh
	for (vector<ItemBufferMesh::FaceHandle>::const_iterator iter = faceIter->GetChildFaceHandles().begin(); iter != faceIter->GetChildFaceHandles().end(); iter++)
	{
		ItemBufferMesh::ConstFaceIter childFaceIter(_mesh.GetItemBufferMesh(), *iter);

		PushDrawCall quads(GL_QUADS);
		int i = 0;
		for(ItemBufferMesh::ConstFaceVertexIter item_fv_it = _mesh.GetItemBufferMesh().cfv_iter(childFaceIter.handle()); item_fv_it; ++item_fv_it, i++)
		{
			//These are the texture coordinates of the face's tesselated components.
			glTexCoord2fv(childFaceIter->GetTexCoord(i).data());
			//The tesselated face's vertexes get projected back into the item buffer
			glVertex3fv(_mesh.GetItemBufferMesh().point(item_fv_it).data());
		}
	}
}

void Radiosity::UpdateShootIndices(void)
{
	if(_shootIndex[Width] < (_shootSize[Width] - 1))
	{
		_shootIndex[Width]++;
	}
	else
	{
		_shootIndex[Width] = 0;
		if(_shootIndex[Height] < (_shootSize[Height] - 1))
		{
			_shootIndex[Height]++;
		}
		else
		{
			_shootIndex[Width] = _shootSize[Width];
			_shootIndex[Height] = _shootSize[Height];

			ClearShootTextures(_shooter);
			RegenerateDeltaMips();
			if(_parameters->GetDisplayAmbient())
			{
				DrawAmbientValue();
			}
		}
	}
}

const RadiosityMesh::FaceIter Radiosity::GetNextShooter(void)
{
	Texture2D nextShooterId(ItemBuffer, GL_LUMINANCE32I_EXT, GL_LUMINANCE_INTEGER_EXT, GL_INT, 1, 1);
	LargestEnergyTest(nextShooterId);
	//Reconstruct the Id to get the face index in the mesh list
	return RadiosityMesh::FaceIter(_mesh, _mesh.GetFaceHandle(ReadShooterId(nextShooterId)));
}

void Radiosity::LargestEnergyTest(const Texture2D& nextShooterId)
{
	PushAttribute pointBit(GL_POINT_BIT);
	PushAttribute viewportBit(GL_VIEWPORT_BIT);
	PushAttribute colorBufferBit(GL_COLOR_BUFFER_BIT);
	PushAttribute transformBit(GL_TRANSFORM_BIT);
	PushAttribute textureBit(GL_TEXTURE_BIT);
	PushMatrix modelview(GL_MODELVIEW);
	PushShader nextShooter(_nextShooterShader);
	PushFBOAttachment nextShooterIdAttachment(_FBO, GL_COLOR_ATTACHMENT0_EXT, nextShooterId);
	PushFBOAttachment nextShooterRBO(_FBO, GL_DEPTH_ATTACHMENT_EXT, _nextShooterRBO);

	//Clipping planes don't matter here since we're manually setting the depth value
	glViewport(0, 0, 1, 1);
	gluOrtho2D(0, 1, 0, 1);
	glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
	glPointSize(5);

	glClearColorIiEXT(-1, -1, -1, 1);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	float farPlane = 10000.0f;
	_nextShooterShader.SetUniform1f("farPlane", farPlane);
	//If you're doing progressive refinement, you always use the stub, if you're not doing progressive refinement, you use it for all non-radiosity shooting modes
	bool isProgressiveRefinementAlgorithm = (_parameters->GetRadiosityAlgorithm() == ProgressiveRefinement);
	bool isShootingImportance = ((_parameters->GetRadiosityAlgorithm() != ProgressiveRefinement) && (_shootMode == ImportanceDriven));
	if(isProgressiveRefinementAlgorithm || isShootingImportance)
	{
		_IjStub.BindTexture(GL_TEXTURE1);
		_nextShooterShader.SetUniform1i("weightTopLevel", 0);
	}

	for(RadiosityMesh::FaceIter faceIter = _mesh.faces_begin(); faceIter != _mesh.faces_end(); ++faceIter)
	{
		if(faceIter->GetHasUnshotRadiosity(_shootMode))
		{
			faceIter->GetRadiosityTextureVariableDelta(_shootMode).BindTexture(GL_TEXTURE0);
			_nextShooterShader.SetUniform1i("deltaTopLevel", faceIter->GetRadiosityTextureVariableDelta(_shootMode).GetTopLevel());

			bool isShootingRadiosity = (_parameters->GetRadiosityAlgorithm() != ProgressiveRefinement) && (_shootMode == ProgressiveRefinement);
			if(isShootingRadiosity)
			{
				faceIter->GetRadiosityTextureVariable(Ij).BindTexture(GL_TEXTURE1);
				_nextShooterShader.SetUniform1i("weightTopLevel", faceIter->GetRadiosityTextureVariable(Ij).GetTopLevel());
			}

			_nextShooterShader.SetUniform1f("faceResolution", faceIter->GetFaceArea());
			//Draw the face Id as the color.  The fragment shader sets the depth as the energy reciprocal
			_nextShooterShader.SetUniform1i("faceId", faceIter.handle().idx());

			PushDrawCall points(GL_POINTS);
			glVertex2f(0.5f, 0.5f);
		}
	}
}

GLint Radiosity::ReadShooterId(const Texture2D& nextShooterId)
{
	PushAttribute pixelModeBit(GL_PIXEL_MODE_BIT);
	PushFBOAttachment nextShooterIdAttachment(_FBO, GL_COLOR_ATTACHMENT0_EXT, nextShooterId);

	GLint Id;
	glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
	glReadPixels(0, 0, 1, 1, GL_LUMINANCE_INTEGER_EXT, GL_INT, &Id);
	return Id;
}

void Radiosity::DrawUserViewItemBuffer(void)
{
	PushAttribute colorBufferBit(GL_COLOR_BUFFER_BIT);
	PushAttribute pixelModeBit(GL_PIXEL_MODE_BIT);
	PushAttribute viewportBit(GL_VIEWPORT_BIT);
	PushShader userView(_userViewShader);
	PushFBOAttachment userViewItemBuffer(_FBO, GL_COLOR_ATTACHMENT0_EXT, _userViewItemBuffer);
	PushFBOAttachment userViewItemBufferRBO(_FBO, GL_DEPTH_ATTACHMENT_EXT, _userViewItemBufferRBO);

	glViewport(0, 0, _parameters->GetWindowWidth(), _parameters->GetWindowHeight());
	glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
	_camera->PerspectiveDisplay(_parameters->GetWindowWidth(), _parameters->GetWindowHeight());

	glClearColorIiEXT(-1, -1, -1, 1);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	for(RadiosityMesh::FaceIter faceIter = _mesh.faces_begin(); faceIter != _mesh.faces_end(); ++faceIter)
	{
		_userViewShader.SetUniform1i("faceId", faceIter.handle().idx());
		PushQuery query(faceIter->GetUserViewOcclusionQuery());
		PushDrawCall quads(GL_QUADS);
		for(RadiosityMesh::FaceVertexIter faceVertexIter = _mesh.fv_iter(faceIter.handle()); faceVertexIter; ++faceVertexIter)
		{
			glVertex3fv(_mesh.point(faceVertexIter).data());
		}
	}
}

void Radiosity::UserViewShade()
{
	PushAttribute colorBufferBit(GL_COLOR_BUFFER_BIT);
	PushAttribute textureBit(GL_TEXTURE_BIT);
	PushAttribute viewportBit(GL_VIEWPORT_BIT);
	PushShader importance(_importanceShader);

	array<GLenum, 3> drawBuffers = {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_COLOR_ATTACHMENT2_EXT};
	glDrawBuffers(static_cast<GLsizei>(drawBuffers.size()), &drawBuffers[0]);
	_userViewItemBuffer.BindTexture(GL_TEXTURE0);

	Vec3f shootPosition = Vec3f(static_cast<float>(_camera->GetPosition()[0]), static_cast<float>(_camera->GetPosition()[1]), static_cast<float>(_camera->GetPosition()[2]));
	Vec3f shootDirectionVector = Vec3f(static_cast<float>(_camera->GetAim()[0]), static_cast<float>(_camera->GetAim()[1]), static_cast<float>(_camera->GetAim()[2]));
	_importanceShader.SetUniform3fv("shootPosition", shootPosition.data());
	_importanceShader.SetUniform3fv("shootDirectionVector", ((shootDirectionVector - shootPosition).normalize()).data());

	//Go through each face in the mesh
	for(RadiosityMesh::FaceIter faceIter = _mesh.faces_begin(); faceIter != _mesh.faces_end(); ++faceIter)
	{
		if(!faceIter->GetIsVisibleUserView())
		{
			continue;
		}

		Vec2i textureSize = faceIter->GetTextureSize(Ij);
		glViewport(0, 0, textureSize[Width], textureSize[Height]);
		PushFBOAttachment2DRW IjAttachment(_FBO, GL_COLOR_ATTACHMENT0_EXT, faceIter->GetRadiosityTextureVariable(Ij));
		PushFBOAttachment2DRW DeltaIjAttachment(_FBO, GL_COLOR_ATTACHMENT1_EXT, faceIter->GetRadiosityTextureVariable(DeltaIj));
		PushFBOAttachment2DRW RjAttachment(_FBO, GL_COLOR_ATTACHMENT2_EXT, faceIter->GetRadiosityTextureVariable(Rj));
		faceIter->GetRadiosityTextureVariable(Ij).BindTexture(GL_TEXTURE1);
		faceIter->GetRadiosityTextureVariable(DeltaIj).BindTexture(GL_TEXTURE2);
		faceIter->GetRadiosityTextureVariable(Rj).BindTexture(GL_TEXTURE3);

		_importanceShader.SetUniform3fv("receiverNormal", _mesh.normal(faceIter).data());
		_importanceShader.SetUniform1i("faceId", faceIter.handle().idx());

		PushDrawCall quads(GL_QUADS);
		RadiosityMesh::FaceVertexIter faceVertexIter;
		array<GLshort[2], 4>::const_iterator texCoordsIter;
		for(faceVertexIter = _mesh.fv_iter(faceIter.handle()), texCoordsIter = _normalizedCoords.begin(); faceVertexIter; ++faceVertexIter, texCoordsIter++)
		{
			glTexCoord2sv(*texCoordsIter);
			//The face's coordinates will go here, which get projected back into the item buffer
			glVertex3fv(_mesh.point(faceVertexIter).data());
		}

		for(int i = Ij; i <= Rj; i++)
		{
			faceIter->GetRadiosityTextureVariable((RadiosityTexture)i).SwapReadWrite();
		}

		faceIter->SetHasUnshotRadiosity(true, ImportanceDriven);
	}
}

void Radiosity::ChangeView(void)
{
	_importanceShootFlag = true;
	if(_parameters->GetRadiosityAlgorithm() == FovDriven)
	{
		_fovChangedFlag = true;
		_fovDecayAngle = 0;
	}
}

float Radiosity::GetCurrentFov(void)
{
	return static_cast<float>(_parameters->GetFovAngle() + _fovDecayAngle);
}

void Radiosity::RegenerateDeltaMips(void)
{
	for(RadiosityMesh::FaceIter faceIter = _mesh.faces_begin(); faceIter != _mesh.faces_end(); ++faceIter)
	{
		if(faceIter->GetHasDirtyMips())
		{
			faceIter->GetRadiosityTextureVariable(DeltaBj).RegenerateMipmap();
			if(_parameters->GetRadiosityAlgorithm() != ProgressiveRefinement)
			{
				faceIter->GetRadiosityTextureVariable(DeltaIj).RegenerateMipmap();
			}
			faceIter->SetHasDirtyMips(false);
		}
	}
}

void Radiosity::ClearShootTextures(const RadiosityMesh::FaceIter& shooter)
{
	PushAttribute textureBit(GL_TEXTURE_BIT);
	PushAttribute viewportBit(GL_VIEWPORT_BIT);
	PushAttribute colorBufferBit(GL_COLOR_BUFFER_BIT);
	PushFBOAttachment delta(_FBO, GL_COLOR_ATTACHMENT0_EXT, shooter->GetRadiosityTextureVariableDelta(_shootMode));

	Vec2i textureSize = shooter->GetTextureSize(_shootMode);
	glViewport(0, 0, textureSize[Width], textureSize[Height]);

	//Clear out shooter's unshot face and turn off its unshot flag
	glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
	glClear(GL_COLOR_BUFFER_BIT);
	shooter->SetHasUnshotRadiosity(false, _shootMode);
}

void Radiosity::LoadShootParameters()
{
	PushAttribute pixelModeBit(GL_PIXEL_MODE_BIT);
	PushAttribute viewportBit(GL_VIEWPORT_BIT);
	PushAttribute transformBit(GL_TRANSFORM_BIT);
	PushAttribute colorBufferBit(GL_COLOR_BUFFER_BIT);
	PushMatrix modelview(GL_MODELVIEW);

	//Need the size of the texture doing the delta shooting, based on the mip we're shooting from
	int shootLevel = min(_parameters->GetShootLevel(), _shooter->GetRadiosityTextureVariableDelta(_shootMode).GetTopLevel());
	_shootSize = _shooter->GetTextureSize(_shootMode, shootLevel);
	_shootPositions.resize(_shootSize[Width] * _shootSize[Height] * 3);
	_shootTexCoords.resize(_shootSize[Width] * _shootSize[Height] * 3);
	Texture2D shootParameterData(Bj, GL_RGB16F_ARB, GL_RGB, GL_FLOAT, _shootSize[Width], _shootSize[Height]);
	PushFBOAttachment shootParameterDataAttachment(_FBO, GL_COLOR_ATTACHMENT0_EXT, shootParameterData);

	glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
	glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
	glViewport(0, 0, _shootSize[Width], _shootSize[Height]);
	gluOrtho2D(0, 1, 0, 1);

	DrawShootParameterData(Positions);
	glReadPixels(0, 0, _shootSize[Width], _shootSize[Height], GL_RGB, GL_FLOAT, &_shootPositions[0]);

	DrawShootParameterData(TexCoords);
	glReadPixels(0, 0, _shootSize[Width], _shootSize[Height], GL_RGB, GL_FLOAT, &_shootTexCoords[0]);
}

void Radiosity::DrawShootParameterData(const ShootParameterData dataType)
{
	array<GLshort[2], 4>::const_iterator texCoordsIter;
	RadiosityMesh::FaceVertexIter faceVertexIter;

	PushDrawCall quads(GL_QUADS);
	for(faceVertexIter = _mesh.fv_iter(_shooter.handle()), texCoordsIter = _normalizedCoords.begin(); faceVertexIter; ++faceVertexIter, texCoordsIter++)
	{
		glColor3fv(GetShootParameterData(dataType, faceVertexIter, texCoordsIter));
		glVertex2sv(*texCoordsIter);
	}
}

float* const Radiosity::GetShootParameterData(const ShootParameterData dataType, RadiosityMesh::FaceVertexIter faceVertexIter, array<GLshort[2], 4>::const_iterator texCoordsIter)
{
	array<float, 3> shootParameterData;

	switch(dataType)
	{
	case Positions:
		{
			float* data = _mesh.point(faceVertexIter).data();
			array<float, 3> dataArray = {data[0], data[1], data[2]};
			shootParameterData = dataArray;
		}
		break;
	case TexCoords:
		{
			array<float, 3> dataArray = {(*texCoordsIter)[0], (*texCoordsIter)[1], 0};
			shootParameterData = dataArray;
		}
		break;
	};

	return &shootParameterData[0];
}

void Radiosity::DrawAmbientValue(void)
{
	PushAttribute viewportBit(GL_VIEWPORT_BIT);
	PushAttribute colorBufferBit(GL_COLOR_BUFFER_BIT);
	PushAttribute textureBit(GL_TEXTURE_BIT);
	PushAttribute transformBit(GL_TRANSFORM_BIT);
	PushMatrix modelview(GL_MODELVIEW);
	PushShader ambient(_ambientShader);
	PushFBOAttachment ambientValueAttachment(_FBO, GL_COLOR_ATTACHMENT0_EXT, _ambient);

	//Set up the 1x1 buffer
	glViewport(0, 0, 1, 1);
	gluOrtho2D(0, 1, 0, 1);

	//Clear out the ambient computation texture
	glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
	glClearColor(0, 0, 0, 1);
	glClear(GL_COLOR_BUFFER_BIT);

	for(RadiosityMesh::FaceIter faceIter = _mesh.faces_begin(); faceIter != _mesh.faces_end(); ++faceIter)
	{
		//Go through all the faces and see if the face has unshot radiosity
		if(faceIter->GetHasUnshotRadiosity(ProgressiveRefinement))
		{
			PushFBOAttachment2DRW ambientValueAttachment(_FBO, GL_COLOR_ATTACHMENT0_EXT, _ambient);
			_ambient.BindTexture(GL_TEXTURE0);
			faceIter->GetRadiosityTextureVariable(DeltaBj).BindTexture(GL_TEXTURE1);

			//Set the fractional area to be the face's area divided by the total area
			_ambientShader.SetUniform1f("areaFraction", faceIter->GetFaceArea()/_AjSum);
			_ambientShader.SetUniform1i("topLevel", faceIter->GetRadiosityTextureVariable(Bj).GetTopLevel());

			PushDrawCall points(GL_POINTS);
			glVertex2f(0.5f, 0.5f);

			_ambient.SwapReadWrite();
		}
	}
}

void Radiosity::InitializeFaces(void)
{
	PushAttribute transformBit(GL_TRANSFORM_BIT);
	PushAttribute colorBufferBit(GL_COLOR_BUFFER_BIT);
	PushAttribute viewportBit(GL_VIEWPORT_BIT);
	PushAttribute textureBit(GL_TEXTURE_BIT);
	PushMatrix modelview(GL_MODELVIEW);

	gluOrtho2D(0, 1, 0, 1);
	InitializeEmissivity();
	//If we're doing importance driven, clear out those textures
	if(_parameters->GetRadiosityAlgorithm() != ProgressiveRefinement)
	{
		InitializeImportance();
	}
	RegenerateDeltaMips();

	InitializeShootParameters();

	_mesh.update_face_normals();
}

void Radiosity::InitializeEmissivity(void)
{
	//Specifiy that you want the face's radiosity textures to be drawn into
	array<GLenum, 2> drawBuffers = {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT};
	glDrawBuffers(static_cast<GLsizei>(drawBuffers.size()), &drawBuffers[0]);

	//Go through each face
	for (RadiosityMesh::FaceIter faceIter = _mesh.faces_begin(); faceIter != _mesh.faces_end(); ++faceIter)
	{
		//Initialize textures (calls Texture class initializations)
		faceIter->Create(_parameters->GetTexturesPerFace());

		PushFBOAttachment2DRW BjAttachment(_FBO, GL_COLOR_ATTACHMENT0_EXT, faceIter->GetRadiosityTextureVariable(Bj));
		PushFBOAttachment2DRW DeltaBjAttachment(_FBO, GL_COLOR_ATTACHMENT1_EXT, faceIter->GetRadiosityTextureVariable(DeltaBj));

		//Setup a 1-to-1 viewport for texture rendering
		Vec2i textureSize = faceIter->GetTextureSize(Bj);
		glViewport(0, 0, textureSize[Width], textureSize[Height]);

		//Set the emissivity as the color and draw the quad
		auto emissiveColor = _mesh.normal(faceIter).data();
		glClearColor(emissiveColor[0], emissiveColor[1], emissiveColor[2], emissiveColor[3]);
		glClear(GL_COLOR_BUFFER_BIT);

		faceIter->SetHasUnshotRadiosity(_mesh.normal(faceIter).norm() != 0, ProgressiveRefinement);
		for(int i = Bj; i <= DeltaBj; i++)
		{
			faceIter->GetRadiosityTextureVariable((RadiosityTexture)i).SwapReadWrite();
		}
	}
}

void Radiosity::InitializeImportance(void)
{
	array<GLenum, 3> drawBuffers = {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_COLOR_ATTACHMENT2_EXT};
	glDrawBuffers(static_cast<GLsizei>(drawBuffers.size()), &drawBuffers[0]);

	for (RadiosityMesh::FaceIter faceIter = _mesh.faces_begin(); faceIter != _mesh.faces_end(); ++faceIter)
	{
		Vec2i textureSize = faceIter->GetTextureSize(Bj);
		glViewport(0, 0, textureSize[Width], textureSize[Height]);

		PushFBOAttachment2DRW IjAttachment(_FBO, GL_COLOR_ATTACHMENT0_EXT, faceIter->GetRadiosityTextureVariable(Ij));
		PushFBOAttachment2DRW DeltaIjAttachment(_FBO, GL_COLOR_ATTACHMENT1_EXT, faceIter->GetRadiosityTextureVariable(DeltaIj));
		PushFBOAttachment2DRW RjAttachment(_FBO, GL_COLOR_ATTACHMENT2_EXT, faceIter->GetRadiosityTextureVariable(Rj));

		glClear(GL_COLOR_BUFFER_BIT);

		for(int i = Ij; i <= Rj; i++)
		{
			faceIter->GetRadiosityTextureVariable((RadiosityTexture)i).SwapReadWrite();
		}
	}
}

void Radiosity::InitializeShootParameters(void)
{
	Vec2i unshotMipmapSize = _mesh.GetFacePositionsTextureVariable().GetTextureSize();
	glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
	glViewport(0, 0, unshotMipmapSize[Width], unshotMipmapSize[Height]);

	//Go through each face
	for (RadiosityMesh::FaceIter faceIter = _mesh.faces_begin(); faceIter != _mesh.faces_end(); ++faceIter)
	{
		array<GLshort[2], 4>::const_iterator texCoordsIter;
		RadiosityMesh::FaceVertexIter faceVertexIter;

		//Render a quad into the texture array, using the vertex positions as the colors
		PushFBOAttachment positions(_FBO, GL_COLOR_ATTACHMENT0_EXT, _mesh.GetFacePositionsTextureVariable(), faceIter.handle().idx());
		PushDrawCall quads(GL_QUADS);
		for(faceVertexIter = _mesh.fv_iter(faceIter.handle()), texCoordsIter = _normalizedCoords.begin(); faceVertexIter; ++faceVertexIter, texCoordsIter++)
		{
			glColor3fv(_mesh.point(faceVertexIter).data());
			glVertex2sv(*texCoordsIter);
		}
	}

	_mesh.GetFacePositionsTextureVariable().RegenerateMipmap();
}

void Radiosity::InitializeShaders(void)
{
	VertexShaderResource commonVertex(IDR_COMMON_VERT, "Common Code Vertex Shader");
	VertexShaderResource passthroughVertex(IDR_PASSTHROUGH_VERT, "Passthrough Vertex Shader");
	VertexShaderResource stereographicVertex(IDR_STEREOGRAPHIC_VERT, "Stereographic Vertex Shader");
	VertexShaderResource hemisphereVertex(IDR_HEMISPHERE_VERT, "Hemishphere Vertex Shader");
	VertexShaderResource importanceVertex(IDR_IMPORTANCE_VERT, "Importance Vertex Shader");
	VertexShaderResource unshotVectorsGatherVertex(IDR_UNSHOT_VECTORS_GATHER_VERT, "Unshot Vectors Gather Vertex Shader");

	FragmentShaderResource commonFragment(IDR_COMMON_FRAG, "Common Code Fragment Shader");
	FragmentShaderResource passthroughFragment(IDR_PASSTHROUGH_FRAG, "Passthrough Fragment Shader");
	FragmentShaderResource displayDebugFragment(IDR_DISPLAY_DEBUG_FRAG, "Display Debug Fragment Shader");
	FragmentShaderResource displayFragment(IDR_DISPLAY_FRAG, "Display Fragment Shader");
	FragmentShaderResource ambientFragment(IDR_COMPUTE_AMBIENT_FRAG, "Compute Ambient Fragment Shader");
	FragmentShaderResource nextShooterFragment(IDR_NEXT_SHOOTER_FRAG, "Next Shooter Fragment Shader");
	FragmentShaderResource hemisphereTextureFragment(IDR_HEMISPHERE_TEXTURE_FRAG, "Hemisphere Texture Fragment Shader");
	FragmentShaderResource hemispherePointFragment(IDR_HEMISPHERE_POINT_FRAG, "Hemisphere Point Fragment Shader");
	FragmentShaderResource importanceFragment(IDR_IMPORTANCE_FRAG, "Importance Fragment Shader");
	FragmentShaderResource radiosityIdFragment(IDR_RADIOSITY_ID_FRAG, "Radiosity Id Fragment Shader");
	FragmentShaderResource unshotVectorsGatherFragment(IDR_UNSHOT_VECTORS_GATHER_FRAG, "Unshot Vectors Gather Fragment Shader");
	FragmentShaderResource unshotVectorsRetrieveFragment(IDR_UNSHOT_VECTORS_RETRIEVE_FRAG, "Unshot Vectors Retrieve Fragment Shader");

	//Initialize all shader objects.  Classes keep track of bookkeeping for shaders
	vector<ShaderResource*> displayShader;
	displayShader.push_back(&passthroughVertex);
	displayShader.push_back(_parameters->GetDisplayVisualization() ? &displayDebugFragment : &displayFragment);
	_displayShader.Create(displayShader);
	_displayShader.Enable();
	_displayShader.SetUniform1i("BjSampler", 0);
	_displayShader.SetUniform1i("ambientSampler", 1);
	_displayShader.Enable(false);

	vector<ShaderResource*> ambientShader;
	ambientShader.push_back(&passthroughVertex);
	ambientShader.push_back(&ambientFragment);
	_ambientShader.Create(ambientShader);
	_ambientShader.Enable();
	_ambientShader.SetUniform1i("ambientSampler", 0);
	_ambientShader.SetUniform1i("deltaBjSampler", 1);
	_ambientShader.Enable(false);

	vector<ShaderResource*> nextShooterShader;
	nextShooterShader.push_back(&passthroughVertex);
	nextShooterShader.push_back(&nextShooterFragment);
	_nextShooterShader.Create(nextShooterShader);
	_nextShooterShader.Enable();
	_nextShooterShader.SetUniform1i("deltaSampler", 0);
	_nextShooterShader.SetUniform1i("weightSampler", 1);
	glBindFragDataLocationEXT(_nextShooterShader.GetProgramId(), 0, "faceIdOut");
	_nextShooterShader.Enable(false);

	vector<ShaderResource*> stereographicShader;
	stereographicShader.push_back(&stereographicVertex);
	stereographicShader.push_back(&commonVertex);
	stereographicShader.push_back(&radiosityIdFragment);
	_stereographicShader.Create(stereographicShader);
	_stereographicShader.Enable();
	_stereographicShader.SetUniform1f("nearPlane", 0.05f);
	_stereographicShader.SetUniform1f("farPlane", _mesh.GetModelSpanDistance() * 1.25f);
	glBindFragDataLocationEXT(_stereographicShader.GetProgramId(), 0, "faceIdOut");
	_stereographicShader.Enable(false);

	vector<ShaderResource*> userViewShader;
	userViewShader.push_back(&passthroughVertex);
	userViewShader.push_back(&radiosityIdFragment);
	_userViewShader.Create(userViewShader);
	_userViewShader.Enable();
	glBindFragDataLocationEXT(_userViewShader.GetProgramId(), 0, "faceIdOut");
	_userViewShader.Enable(false);

	vector<ShaderResource*> hemisphereTextureShader;
	hemisphereTextureShader.push_back(&hemisphereVertex);
	hemisphereTextureShader.push_back(&commonVertex);
	hemisphereTextureShader.push_back(&hemisphereTextureFragment);
	hemisphereTextureShader.push_back(&commonFragment);
	_hemisphereTextureShader.Create(hemisphereTextureShader);
	_hemisphereTextureShader.Enable();
	_hemisphereTextureShader.SetUniform1i("itemBufferSampler", 0);
	_hemisphereTextureShader.SetUniform1i("BjSampler", 1);
	_hemisphereTextureShader.SetUniform1i("deltaBjSampler", 2);
	_hemisphereTextureShader.SetUniform1i("shooterDeltaBjSampler", 3);
	_hemisphereTextureShader.SetUniform1f("nearPlane", 0.05f);
	_hemisphereTextureShader.SetUniform1f("farPlane", _mesh.GetModelSpanDistance() * 1.25f);
	_hemisphereTextureShader.Enable(false);

	vector<ShaderResource*> hemispherePointShader;
	hemispherePointShader.push_back(&hemisphereVertex);
	hemispherePointShader.push_back(&commonVertex);
	hemispherePointShader.push_back(&hemispherePointFragment);
	hemispherePointShader.push_back(&commonFragment);
	_hemispherePointShader.Create(hemispherePointShader);
	_hemispherePointShader.Enable();
	_hemispherePointShader.SetUniform1i("itemBufferSampler", 0);
	_hemispherePointShader.SetUniform1i("BjSampler", 1);
	_hemispherePointShader.SetUniform1i("deltaBjSampler", 2);
	_hemispherePointShader.SetUniform1i("shooterDeltaBjSampler", 3);
	_hemispherePointShader.SetUniform1i("becomingVisibleVectorsSampler", 4);
	_hemispherePointShader.SetUniform1i("facePositionsSampler", 5);
	_hemispherePointShader.SetUniform1f("nearPlane", 0.05f);
	_hemispherePointShader.SetUniform1f("farPlane", _mesh.GetModelSpanDistance() * 1.25f);
	_hemispherePointShader.SetUniform1i("unshotShooterLevel", _parameters->GetUnshotMipmapShootLevel());
	_hemispherePointShader.Enable(false);

	vector<ShaderResource*> importanceShader;
	importanceShader.push_back(&importanceVertex);
	importanceShader.push_back(&commonVertex);
	importanceShader.push_back(&importanceFragment);
	importanceShader.push_back(&commonFragment);
	_importanceShader.Create(importanceShader);
	_importanceShader.Enable();
	_importanceShader.SetUniform1i("itemBufferSampler", 0);
	_importanceShader.SetUniform1i("IjSampler", 1);
	_importanceShader.SetUniform1i("deltaIjSampler", 2);
	_importanceShader.SetUniform1i("RjSampler", 3);
	_importanceShader.Enable(false);

	vector<ShaderResource*> unshotVectorsGatherShader;
	unshotVectorsGatherShader.push_back(&unshotVectorsGatherVertex);
	unshotVectorsGatherShader.push_back(&unshotVectorsGatherFragment);
	unshotVectorsGatherShader.push_back(&commonFragment);
	_unshotVectorsGatherShader.Create(unshotVectorsGatherShader);
	_unshotVectorsGatherShader.Enable();
	_unshotVectorsGatherShader.SetUniform1i("unshotVectorsSampler", 0);
	_unshotVectorsGatherShader.SetUniform1i("fovVisibilityVectorSampler", 1);
	_unshotVectorsGatherShader.SetUniform1i("facePositionsSampler", 2);
	_unshotVectorsGatherShader.SetUniform1i("shooterDeltaBjSampler", 3);
	_unshotVectorsGatherShader.SetUniform1i("unshotShooterLevel", _parameters->GetUnshotMipmapShootLevel());
	_unshotVectorsGatherShader.Enable(false);

	vector<ShaderResource*> unshotVectorsRetrieveShader;
	unshotVectorsRetrieveShader.push_back(&passthroughVertex);
	unshotVectorsRetrieveShader.push_back(&unshotVectorsRetrieveFragment);
	unshotVectorsRetrieveShader.push_back(&commonFragment);
	_unshotVectorsRetrieveShader.Create(unshotVectorsRetrieveShader);
	_unshotVectorsRetrieveShader.Enable();
	_unshotVectorsRetrieveShader.SetUniform1i("unshotVectorsSampler", 0);
	_unshotVectorsRetrieveShader.SetUniform1i("fovVisibilityVectorSampler", 1);
	_unshotVectorsRetrieveShader.Enable(false);
}

void Radiosity::InitializeMatrixes()
{
	PushAttribute transformBit(GL_TRANSFORM_BIT);
	PushMatrix(GL_TEXTURE);

	//Initialize the texture transform matrixes used to do PSM
	glActiveTexture(GL_TEXTURE0);
	glLoadIdentity();
	glTranslatef(0.5, 0.5, 0.5);
	glScalef(0.5, 0.5, 0.5);

	glActiveTexture(GL_TEXTURE1);
	glLoadIdentity();
	gluOrtho2D(0, 1, 0, 1);
}

void Radiosity::InitializeBuffers(void)
{
	_hemisphereItemBuffer.Create(ItemBuffer, GL_LUMINANCE32I_EXT, GL_LUMINANCE_INTEGER_EXT, GL_INT, _parameters->GetItemBufferResolution(), _parameters->GetItemBufferResolution());
	_userViewItemBuffer.Create(ItemBuffer, GL_LUMINANCE32I_EXT, GL_LUMINANCE_INTEGER_EXT, GL_INT, _parameters->GetWindowWidth(), _parameters->GetWindowHeight());

	_nextShooterRBO.Set(GL_DEPTH_COMPONENT32, 1, 1);
	_hemisphereItemBufferRBO.Set(GL_DEPTH_COMPONENT32, _parameters->GetItemBufferResolution(), _parameters->GetItemBufferResolution());
	_userViewItemBufferRBO.Set(GL_DEPTH_COMPONENT32, _parameters->GetWindowWidth(), _parameters->GetWindowHeight());

	_fovVisibleVectorPBO.Create(_mesh.n_faces());
}

void Radiosity::InitializeTextures(void)
{
	PushAttribute viewportBit(GL_VIEWPORT_BIT);
	PushAttribute colorBufferBit(GL_COLOR_BUFFER_BIT);

	_IjStub.Create(DeltaIj, GL_RGB16F_ARB, GL_RGB, GL_FLOAT, 1, 1);
	_ambient.Create(Bj, GL_RGB16F_ARB, GL_RGB, GL_FLOAT, 1, 1);

	glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
	glViewport(0, 0, 1, 1);

	glClearColor(1, 1, 1, 1);
	PushFBOAttachment IjStub(_FBO, GL_COLOR_ATTACHMENT0_EXT, _IjStub);
	glClear(GL_COLOR_BUFFER_BIT);
	_IjStub.RegenerateMipmap();

	if(_parameters->GetRadiosityAlgorithm() == FovDriven)
	{
		Vec2i unshotMipmapSize = _mesh.GetFacePositionsTextureVariable().GetTextureSize(_parameters->GetUnshotMipmapShootLevel());
		int unshotMipmapArea = unshotMipmapSize[Width] * unshotMipmapSize[Height];
		_unshotVectors.Create(ItemBuffer, GL_RGB16F_ARB, GL_RGB, GL_FLOAT, (_mesh.n_faces() * unshotMipmapArea) + 1, _mesh.n_faces() + 1);
		_becomingVisibleVectors.Create(ItemBuffer, GL_RGB16F_ARB, GL_RGB, GL_FLOAT, (_mesh.n_faces() * unshotMipmapArea) + 1, _mesh.n_faces() + 1);
		_fovVisibleVector.Create(ItemBuffer, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE, _mesh.n_faces(), 1);

		glClearColor(0, 0, 0, 1);
		PushFBOAttachmentRectRW unshotVectors(_FBO, GL_COLOR_ATTACHMENT0_EXT, _unshotVectors);
		glClear(GL_COLOR_BUFFER_BIT);
		PushFBOAttachmentRectRW becomingVisibleVectors(_FBO, GL_COLOR_ATTACHMENT0_EXT, _becomingVisibleVectors);
		glClear(GL_COLOR_BUFFER_BIT);
		PushFBOAttachmentRectRW fovVisibilityVector(_FBO, GL_COLOR_ATTACHMENT0_EXT, _fovVisibleVector);
		glClear(GL_COLOR_BUFFER_BIT);

		_unshotVectors.SwapReadWrite();
		_becomingVisibleVectors.SwapReadWrite();
		_fovVisibleVector.SwapReadWrite();
	}

	_FBO.Unattach(GL_COLOR_ATTACHMENT0_EXT);
}

void Radiosity::InitializeOpenGL(void)
{
	glClearColor(0, 0, 0, 1);

	glPolygonOffset(2, 4);
	glShadeModel(GL_SMOOTH);

	glFrontFace(GL_CCW);      
	glCullFace(GL_BACK);
	glEnable(GL_CULL_FACE);

	glEnable(GL_DEPTH_TEST);
	glEnable(GL_NORMALIZE);

	glClampColorARB(GL_CLAMP_VERTEX_COLOR_ARB, GL_FALSE);
	glClampColorARB(GL_CLAMP_FRAGMENT_COLOR_ARB, GL_FALSE);
	glClampColorARB(GL_CLAMP_READ_COLOR_ARB, GL_FALSE);

	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
	glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
}

void Radiosity::InitializeAmbientParameters(void)
{
	//Compute total area for ambient term use
	_AjSum = 0;
	for (RadiosityMesh::FaceIter faceIter = _mesh.faces_begin(); faceIter != _mesh.faces_end(); ++faceIter)
	{
		_AjSum += faceIter->GetFaceArea();
	}

	//Compute overall interreflection factor
	Vec3f rho_avg(0, 0, 0);
	for (RadiosityMesh::FaceIter faceIter = _mesh.faces_begin(); faceIter != _mesh.faces_end(); ++faceIter)
	{
		rho_avg += (faceIter->GetFaceReflectivity() * faceIter->GetFaceArea());
	}

	rho_avg /= _AjSum;
	_R = Vec3f(Vec3f(1.0, 1.0, 1.0) / (Vec3f(1.0, 1.0, 1.0) - rho_avg));

	PushShader displayShader(_displayShader);
	_displayShader.SetUniform3fv("R", _R.data());
}

void Radiosity::OnFrameDrawTimer(void)
{
	_frameDrawFlag = true;
}

void Radiosity::OnFovDecayTimer(void)
{
	_fovChangedFlag = true;
	_fovDecayAngle += 5;
}
